﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace gemGenerator
{
    //one ring of vertices has the same height and radius
    class VertexRing : ICloneable
    {
        enum AtomType { Number, Symbol, List };
        //atom in the expression tree - either inner node (list of vals) or leaf (number or operator) 
        class atom
        {
            public double val = 0;
            public List<atom> ats = null;
            public AtomType ty;
            public char op = 'e';
            public atom(double d)
            {
                val = d;
                ty = AtomType.Number;
            }
            public atom(char o)
            {
                op = o;
                ty = AtomType.Symbol;
            }
            public atom(List<atom> l)
            {
                ats = l;
                ty = AtomType.List;
            }
            public bool IsOperator(char o)
            {
                return ty == AtomType.Symbol && op == o;
            }


        }
        class node
        {
            atom a; // > < , x
            node l = null;
            node r = null;
            static bool fail = false;
            public void GeneratePhis(out LinkedList<double> positions, out double length)
            {//go through the generated tree and make a sequence vertices
                double cursor = 0.0;
                double correction = 0.0;
                positions = new LinkedList<double>();
                GeneratePhis(positions, ref cursor, ref correction);
                length = cursor + correction;
                if (length == 0) length = 1;
            }
            void GeneratePhis(LinkedList<double> positions, ref double cursor, ref double correction)
            {  //recursive vertex generation
                if (a.ty == AtomType.Number)
                {
                    positions.AddLast(cursor);
                    cursor += GetNumber();
                }
                else
                {//ty = Symbol
                    switch (a.op)
                    {
                        case ',': l.GeneratePhis(positions, ref cursor, ref correction);
                            r.GeneratePhis(positions, ref cursor, ref correction);
                            break;
                        case 'x':
                            {
                                int iter = (int)l.GetNumber();
                                for (int i = 0; i < iter; ++i)
                                    r.GeneratePhis(positions, ref cursor, ref correction);
                                break;
                            }
                        case '<':
                            cursor -= r.GetNumber();
                            correction += r.GetNumber();
                            break;
                        case '>':
                            cursor += r.GetNumber();
                            correction -= r.GetNumber();
                            break;
                    }

                }
            }

            static void Fail()
            {
                fail = true;
            }
            public static bool HasFailed() { return fail; }

            bool IsNumber()
            {
                return a != null && a.ty == AtomType.Number;
            }
            double GetNumber()
            {
                if (a == null) return 0.0;
                return a.val;
            }
            bool SplitByOp(List<atom> from, char op, out List<atom> left, out atom aop, out List<atom> right)
            { // (...a...)x(b...) -> ...a..., x, b...
                for (int i = 0; i < from.Count; ++i)
                {
                    if (from[i].IsOperator(op))
                    {
                        left = from.GetRange(0, i);
                        aop = from[i];
                        right = from.GetRange(i + 1, from.Count - i - 1);
                        return true;
                    }
                }
                left = right = null;
                aop = null;
                return false;
            }
            void create(List<atom> bratoms)
            { //create tree of the expression
                if (bratoms == null) Fail();
                if (fail) return;
                if (bratoms.Count < 1)
                {
                    Fail();
                    return;
                }
                if (bratoms.Count == 1)
                {
                    a = bratoms[0];
                    if (a.ty == AtomType.List) create(a.ats);
                    else if (a.ty != AtomType.Number)
                        Fail();
                    return;
                }
                else
                {
                    List<atom> left, right;
                    if (SplitByOp(bratoms, ',', out left, out a, out right))
                    {
                        if (left.Count < 1 || right.Count < 1) Fail();
                        l = new node(left);
                        r = new node(right);
                    }
                    else if (SplitByOp(bratoms, 'x', out left, out a, out right))
                    {
                        if (left.Count < 1 || right.Count < 1) Fail();
                        l = new node(left);
                        if (!l.IsNumber()) Fail();
                        r = new node(right);
                    }
                    else if (SplitByOp(bratoms, '<', out left, out a, out right) || SplitByOp(bratoms, '>', out left, out a, out right))
                    {
                        if (left.Count > 0) Fail();
                        if (right.Count < 1) Fail();
                        r = new node(right);
                    }
                }
            }
            public node(string expr)
            {
                fail = false;
                create(BracketHierarchy(ToAtoms(expr)));
            }
            node(List<atom> atoms)
            {
                create(atoms);
            }
            public static List<atom> BracketHierarchy(List<atom> atoms)
            { //make bracketed expressions into single atoms
                if (atoms == null)
                {
                    Fail();
                    return null;
                }
                List<atom> tree = new List<atom>();

                List<atom> sub = new List<atom>();
                int brdepth = 0;
                foreach (atom a in atoms)
                {
                    if (a.IsOperator('('))
                    {
                        if (++brdepth > 1) sub.Add(a);
                    }
                    else if (a.IsOperator(')'))
                    {
                        if (--brdepth == 0)
                        {
                            tree.Add(new atom(BracketHierarchy(sub)));
                            sub.Clear();
                        }
                        else sub.Add(a);
                    }
                    else if (brdepth == 0) tree.Add(a);
                    else sub.Add(a);
                }
                return tree;
            }
            public static List<atom> ToAtoms(string expr)
            {
                expr = expr + " ";//to process final number
                List<atom> atoms = new List<atom>();
                StringBuilder num = new StringBuilder();
                foreach (char c in expr)
                {
                    if (c == '-' || c == '.' || (c >= '0' && c <= '9'))
                        num.Append(c);
                    else
                    {
                        if (num.Length > 0)
                        {
                            string sn = num.ToString();
                            num.Clear();
                            double nval;
                            if (TryParse(sn, out nval))
                                atoms.Add(new atom(nval));
                            else
                            {
                                Fail();
                                return null;
                            }
                        }
                        if ("<>()x,".Contains(c))
                            atoms.Add(new atom(c));
                        else if (c != ' ') //only other allowed character
                        {
                            Fail();
                            return null;
                        }
                    }
                }

                return atoms;
            }
        }
        private static bool TryParse(string str, out double val, double limit = double.PositiveInfinity)
        {
            bool ret = double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out val);
            return (ret && Math.Abs(val) < limit);
        }
        public object Clone()
        {
            return new VertexRing(height, diameter, layout.Clone() as string);
        }
        public VertexRing(double height, double r, string layout)
        {
            this.height = height; this.diameter = r; this.layout = layout;
        }
        double height;
        double diameter;
        string layout;
        bool error = false;
        public string Height
        {
            get { return height.ToString(CultureInfo.InvariantCulture); }
            set
            {
                double conv;
                if (TryParse(value, out conv, 5.0)) height = conv;
            }
        }
        public string Diameter
        {
            get { return diameter.ToString(CultureInfo.InvariantCulture); }
            set
            {
                double conv;
                if (TryParse(value, out conv, 5.0)) diameter = conv;
            }
        }
        public string Layout
        {
            get { return layout; }
            set { layout = value.Replace(" ", ""); }
        }
        public string Coordinates
        {
            get { return Height + " " + Diameter + " " + Layout; }
            //set { }
        }
        public bool CreateVertices(LinkedList<VectorD> vertices, CoordinateTransform transform)
        {//parse layout and add vertices 
            error = false;
            node ring = new node(layout);
            if (node.HasFailed())
            {
                error = true;
                return false;
            }
            LinkedList<double> intervals;
            double length;
            ring.GeneratePhis(out intervals, out length);

            foreach (double phi in intervals)
                vertices.AddLast(transform.Transform(new VectorD(diameter, phi / length * 2 * Math.PI, height)));

            return true;
            //  MessageBox.Show("P");
        }
        public override string ToString()
        {
            if (error)
            {
                return "ERROR";
            }
            return (height > -0.001 ? "Crown:\t"
                                   :  "Pavillion: ")
            + layout.ToString() + ", " + diameter.ToString(CultureInfo.InvariantCulture) + ", " + height.ToString(CultureInfo.InvariantCulture);
        }


    }

}
